package com.project_doctor.handler;


import com.alibaba.fastjson.JSON;
import com.project_doctor.pojo.Doctor;
import com.project_doctor.pojo.Message;
import com.project_doctor.pojo.Conversation;
import com.project_doctor.service.ConversationService;
import com.project_doctor.service.DoctorService;
import com.project_doctor.service.MessageService;
import com.project_doctor.service.WebSocketService;
import com.project_doctor.util.JwtUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.AttributeKey;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.jsonwebtoken.JwtException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CompletableFuture;

@Component
@ChannelHandler.Sharable
public class WebSocketHandler extends SimpleChannelInboundHandler<WebSocketFrame> {
    private static final Logger logger = LoggerFactory.getLogger(WebSocketHandler.class);
    private static final Map<Long, Channel> userChannelMap = new ConcurrentHashMap<>();
    private static final Map<Long, Map<Long, Channel>> userSubscriptions = new ConcurrentHashMap<>();
    private static final AttributeKey<Long> USER_ID_KEY = AttributeKey.newInstance("userId");

    @Autowired private MessageService messageService;
    @Autowired private ConversationService conversationService;
    @Autowired private JwtUtil jwtUtil;
    @Autowired private DoctorService doctorService;
    @Autowired private WebSocketService webSocketService;

    @Value("${netty.websocket.read-timeout:60}")
    private int readTimeout;

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) {
        if (!(frame instanceof TextWebSocketFrame)) {
            sendError(ctx, 400, "不支持的消息类型: " + frame.getClass().getName());
            return;
        }

        String text = ((TextWebSocketFrame) frame).text();
        logger.info("收到消息: {}", text);

        try {
            Map<String, Object> msg = JSON.parseObject(text, HashMap.class);

            // 心跳消息处理
            if (msg.containsKey("type")) {
                if ("ping".equals(msg.get("type"))) {
                    logger.debug("收到心跳消息");
                    ctx.writeAndFlush(new TextWebSocketFrame("{\"type\":\"pong\"}"));
                    return;
                }
                if ("pong".equals(msg.get("type"))) {
                    logger.debug("收到心跳响应");
                    return;
                }
            }

            String action = (String) msg.get("action");
            if (action == null) {
                sendError(ctx, 400, "消息必须包含action字段");
                return;
            }

            switch (action) {
                case "login":
                    handleLogin(ctx, msg);
                    break;
                case "message":
                    handleMessage(ctx, msg);
                    break;
                case "subscribe":
                    handleSubscribe(ctx, msg);
                    break;
                case "unsubscribe":
                    handleUnsubscribe(ctx, msg);
                    break;
                default:
                    sendError(ctx, 400, "未知的消息类型: " + action);
            }
        } catch (Exception e) {
            logger.error("消息处理异常", e);
            sendError(ctx, 400, "消息处理失败: " + e.getMessage());
        }
    }

    // 优化登录逻辑，减少不必要的数据库更新
    private void handleLogin(ChannelHandlerContext ctx, Map<String, Object> msg) {
        String token = (String) msg.get("token");
        try {
            Long userId = Long.parseLong(jwtUtil.getValue(token, "userId"));

            // 修改这里：不再强制关闭旧连接
            Channel oldChannel = userChannelMap.get(userId);
            if (oldChannel != null && oldChannel != ctx.channel()) {
                // 只发送通知，不强制关闭
                oldChannel.writeAndFlush(new TextWebSocketFrame("{\"type\":\"multi_login\"}"));
            }

            // 保存新连接
            userChannelMap.put(userId, ctx.channel());
            ctx.channel().attr(USER_ID_KEY).set(userId);

            // 更新用户状态
            webSocketService.processLogin(userId);

            logger.info("用户 {} 连接成功", userId);
        } catch (JwtException e) {
            logger.error("JWT认证失败: {}", e.getMessage());
            sendError(ctx, 401, "认证失败，请重新登录");
            ctx.close();
        } catch (Exception e) {
            logger.error("登录处理异常", e);
            sendError(ctx, 500, "登录失败，请稍后再试");
            ctx.close();
        }
    }

    private void handleSubscribe(ChannelHandlerContext ctx, Map<String, Object> msg) {
        try {
            Long userId = getUserIdFromContext(ctx);
            Long conversationId = ((Number) msg.get("conversationId")).longValue();

            // 验证对话存在且用户有权限
            Conversation conversation = conversationService.getById(conversationId);
            if (conversation == null ||
                    (!conversation.getStarterId().equals(userId) &&
                            !conversation.getParticipantId().equals(userId))) {
                sendError(ctx, 403, "无权订阅此对话");
                return;
            }

            // 保存订阅
            userSubscriptions.computeIfAbsent(userId, k -> new ConcurrentHashMap<>())
                    .put(conversationId, ctx.channel());

            // 发送成功响应
            Map<String, Object> response = new HashMap<>();
            response.put("code", 200);
            response.put("message", "订阅成功");
            response.put("conversationId", conversationId);
            ctx.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(response)));

            logger.info("用户 {} 成功订阅对话 {}", userId, conversationId);
        } catch (Exception e) {
            logger.error("订阅处理异常", e);
            sendError(ctx, 500, "订阅失败");
        }
    }

    private void handleUnsubscribe(ChannelHandlerContext ctx, Map<String, Object> msg) {
        try {
            Long userId = getUserIdFromContext(ctx);
            if (userId == null) {
                sendError(ctx, 401, "用户未登录");
                return;
            }

            Long conversationId = ((Number) msg.get("conversationId")).longValue();
            if (conversationId == null) {
                sendError(ctx, 400, "缺少conversationId");
                return;
            }

            // 移除用户订阅
            Map<Long, Channel> subscriptions = userSubscriptions.get(userId);
            if (subscriptions != null) {
                subscriptions.remove(conversationId);
                if (subscriptions.isEmpty()) {
                    userSubscriptions.remove(userId);
                }
            }

            sendSuccess(ctx, "取消订阅对话成功");
            logger.info("用户 {} 取消订阅对话 {}", userId, conversationId);

        } catch (Exception e) {
            logger.error("取消订阅对话失败", e);
            sendError(ctx, 500, "取消订阅对话异常");
        }
    }

    private void handleMessage(ChannelHandlerContext ctx, Map<String, Object> msg) {
        try {
            Long senderId = ((Number) msg.get("senderId")).longValue();
            String content = (String) msg.get("content");
            Long conversationId = ((Number) msg.get("conversationId")).longValue();
            int type = ((Number) msg.get("type")).intValue();

            // 检查发送者是否已登录
            if (!userChannelMap.containsKey(senderId)) {
                sendError(ctx, 401, "请先登录");
                return;
            }

            // 创建消息对象
            Message message = new Message();
            message.setSenderId(senderId);
            message.setConversationId(conversationId);
            message.setContent(content);

            // 保存消息，使用final变量确保异步安全
            final Message savedMessage = messageService.saveMessage(message);

            // 更新对话
            conversationService.updateLatestMessage(conversationId, savedMessage);

            // 异步发送消息给订阅者
            CompletableFuture.runAsync(() -> sendMessageToSubscribers(conversationId, savedMessage));

            // 发送成功响应给发送者
            Map<String, Object> successResponse = new HashMap<>();
            successResponse.put("code", 200);
            successResponse.put("message", "消息发送成功");
            successResponse.put("data", savedMessage);
            ctx.channel().writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(successResponse)));

        } catch (Exception e) {
            logger.error("消息处理失败", e);
            sendError(ctx, 500, "消息处理异常");
        }
    }


    private void sendMessageToSubscribers(Long conversationId, Message message) {
        Conversation conversation = conversationService.getById(conversationId);
        if (conversation == null) return;

        Long participant1 = conversation.getStarterId();
        Long participant2 = conversation.getParticipantId();
        Long senderId = message.getSenderId();

        // 构建消息对象
        Map<String, Object> msg = new HashMap<>();
        msg.put("type", "newMessage");

        // 完善消息内容
        Map<String, Object> payload = new HashMap<>();
        payload.put("id", message.getId());
        payload.put("senderId", message.getSenderId());
        payload.put("content", message.getContent());
        payload.put("messageTime", message.getMessageTime());
        payload.put("conversationId", conversationId);

        // 获取发送者信息
        // 获取发送者信息并封装为sender对象
        Doctor sender = doctorService.getById(senderId);
        if (sender != null) {
            Map<String, Object> senderInfo = new HashMap<>();
            senderInfo.put("name", sender.getName());
            senderInfo.put("avatar", sender.getAvatar());
            payload.put("sender", senderInfo);
        }
//        Doctor sender = doctorService.getById(senderId);
//        if (sender != null) {
//            payload.put("senderName", sender.getName());
//            payload.put("senderAvatar", sender.getAvatar());
//        }

        msg.put("payload", payload);

        String jsonMessage = JSON.toJSONString(msg);

//        // 强制发送给两个参与者（即使未订阅）
//        sendToUser(participant1, jsonMessage, conversationId, true);
//        sendToUser(participant2, jsonMessage, conversationId, true);

        //强制发送
        if(senderId.equals(participant1)) {
            sendToUser(participant2, jsonMessage, conversationId, true);
        }else {
            sendToUser(participant1, jsonMessage, conversationId, true);
        }

    }

    private void sendToUser(Long userId, String message, Long conversationId, boolean forceSend) {
        Channel channel = userChannelMap.get(userId);
        if (channel == null || !channel.isActive()) {
            logger.warn("用户 {} 不在线或通道已关闭 (对话 {})", userId, conversationId);
            return;
        }

        // 如果强制发送或用户订阅了该对话
        if (forceSend || isUserSubscribed(userId, conversationId)) {
            channel.writeAndFlush(new TextWebSocketFrame(message));
            logger.info("消息已推送给用户 {} (对话 {})", userId, conversationId);
        } else {
            logger.warn("用户 {} 未订阅对话 {}，不发送消息", userId, conversationId);
        }
    }

    private boolean isUserSubscribed(Long userId, Long conversationId) {
        Map<Long, Channel> userSubs = userSubscriptions.get(userId);
        return userSubs != null && userSubs.containsKey(conversationId);
    }

    // 从上下文中获取用户ID
    private Long getUserIdFromContext(ChannelHandlerContext ctx) {
        return ctx.channel().attr(USER_ID_KEY).get();
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent e = (IdleStateEvent) evt;
            if (e.state() == IdleState.READER_IDLE) {
                Long userId = getUserIdFromContext(ctx);
                logger.info("心跳超时（{}秒），关闭用户 {} 的连接", readTimeout, userId);
                ctx.close();
            }
        }
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        Long userId = getUserIdFromContext(ctx);
        if (userId != null) {
            // 修改这里：只有当没有其他连接时才标记为离线
            boolean hasOtherConnection = userChannelMap.entrySet().stream()
                    .anyMatch(entry -> entry.getKey().equals(userId) && entry.getValue() != ctx.channel());

            if (!hasOtherConnection) {
                userChannelMap.remove(userId);
                userSubscriptions.remove(userId);

                try {
                    Doctor user = doctorService.getById(userId);
                    if (user != null) {
                        user.setStatus("OFFLINE");
                        doctorService.updateById(user);
                        logger.info("用户 {} 已离线", userId);
                    }
                } catch (Exception e) {
                    logger.error("更新用户离线状态失败", e);
                }
            }
        }
        logger.info("连接断开: {}", ctx.channel().id());
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        if (cause instanceof IOException) {
            logger.warn("网络异常，关闭连接: {}", cause.getMessage());
        } else if (cause instanceof JwtException) {
            logger.error("JWT认证异常: {}", cause.getMessage());
            sendError(ctx, 401, "认证失败，请重新登录");
        } else {
            logger.error("未处理的异常，关闭连接", cause);
            sendError(ctx, 500, "服务器内部错误");
        }
        ctx.close();
    }

    private void sendError(ChannelHandlerContext ctx, int code, String message) {
        Map<String, Object> response = new HashMap<>();
        response.put("code", code);
        response.put("message", message);
        ctx.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(response)));
    }

    private void sendSuccess(ChannelHandlerContext ctx, String message) {
        Map<String, Object> response = new HashMap<>();
        response.put("code", 200);
        response.put("message", message);
        ctx.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(response)));
    }
}